﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace LightLibrary {
	public partial class FormCopyFiles : Form {
		// === Public ====================================================================================================
		public FormCopyFiles() {
			InitializeComponent();
		}

		public List<FileInfo> CopyFiles(List<FileInfo> sourceFiles, String destinationPath) {
			Show();
			Update = delegate(int totalFiles, int copiedFiles, long totalBytes, long copiedBytes, string currentFileName) {
				progTotalFiles.Maximum = totalFiles;
				progTotalFiles.Value = copiedFiles;
				progCurrentFile.Maximum = 100;

				if(totalBytes != 0) {
					progCurrentFile.Value = Convert.ToInt32((100f / (totalBytes / 1024f)) * (copiedBytes / 1024f));
				}

				if(totalFiles == copiedFiles && totalBytes == copiedBytes) {
					Hide();
					//Close();
				}
			};

			_totalFiles = sourceFiles.Count;
			var result = new List<FileInfo>(sourceFiles.Count);
			foreach(var fileInfoToCopy in sourceFiles) {
				_currentFilename = fileInfoToCopy.FullName;
				_totalFilesCopied += 1;
				var newFileName = Path.Combine(destinationPath, fileInfoToCopy.Name);
				CopyFile(fileInfoToCopy.FullName, newFileName);
				
				result.Add(new FileInfo(newFileName));
			}
			return result;
		}

		// === Ptivate ====================================================================================================
		private Int32 _totalFiles = 0;
		private Int32 _totalFilesCopied = 0;
		private String _currentFilename;

		private delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 totalFiles, T2 copiedFiles, T3 totalBytes, T4 copiedBytes, T5 currentFileName);

		private Action<int, int, long, long, string> Update;

		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName,
		   CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref Int32 pbCancel,
		   CopyFileFlags dwCopyFlags);

		private delegate CopyProgressResult CopyProgressRoutine(
			long TotalFileSize,				// Размер файла в байтах.
			long TotalBytesTransferred,		// Кол-во перемещенных байт, с начала копирования
			long StreamSize,				// Размер текущего потока, в байтах.
			long StreamBytesTransferred,	// Кол-во перемещенных байт потока, с начала копирования
			uint dwStreamNumber,			// Номер потока
			CopyProgressCallbackReason dwCallbackReason,	// см. MSDN
			IntPtr hSourceFile,				// Хендл исходного файла
			IntPtr hDestinationFile,		// Хендл копируемого файла
			IntPtr lpData);

		private int pbCancel;

		[Flags]
		private enum CopyFileFlags : uint {
			COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
			COPY_FILE_RESTARTABLE = 0x00000002,
			COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
			COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
		}

		private enum CopyProgressResult : uint {
			PROGRESS_CONTINUE = 0,
			PROGRESS_CANCEL = 1,
			PROGRESS_STOP = 2,
			PROGRESS_QUIET = 3
		}

		private enum CopyProgressCallbackReason : uint {
			CALLBACK_CHUNK_FINISHED = 0x00000000,
			CALLBACK_STREAM_SWITCH = 0x00000001
		}

		private void CopyFile(string oldFile, string newFile) {
			CopyFileEx(oldFile, newFile, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);
		}

		private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long StreamByteTrans, uint dwStreamNumber, CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData) {
			if(InvokeRequired)
				Invoke(new CopyProgressRoutine(CopyProgressHandler),
						new Object[] { total, transferred, streamSize, StreamByteTrans, dwStreamNumber, reason, hSourceFile, hDestinationFile, lpData });
			else
				Update(_totalFiles, _totalFilesCopied, total, transferred, _currentFilename);
			return CopyProgressResult.PROGRESS_CONTINUE;
		}
	}
}
